﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;

namespace ProxyServerEmulator
{
    public class CacheKey
    {
        public String AbsoluteUri { get; set; }
        public String UserAgent { get; set; }

        public CacheKey(String requestUri, String userAgent)
        {
            AbsoluteUri = requestUri;
            UserAgent = userAgent;
        }
    }

    public class CacheEntry
    {
        public String AbsoluteUri { get; set; }
        public String UserAgent { get; set; }
        public DateTime? Expires { get; set; }
        public DateTime DateStored { get; set; }
        public Byte[] ResponseBytes { get; set; }
        public HttpStatusCode StatusCode { get; set; }
        public String StatusDescription { get; set; }
        public List<Tuple<String, String>> Headers { get; set; }
        public Boolean FlagRemove { get; set; }
    }

    public class ProxyCache
    {
        private static Hashtable _cache = new Hashtable();
        private static Object _cacheLockObj = new object();
        private static Object _statsLockObj = new object();

        public static CacheEntry GetData(HttpWebRequest request)
        {
            CacheKey key = new CacheKey(request.RequestUri.AbsoluteUri, request.UserAgent);
            if (_cache[key] != null)
            {
                CacheEntry entry = (CacheEntry)_cache[key];
                if (entry.FlagRemove || (entry.Expires.HasValue && entry.Expires < DateTime.Now))
                {
                    entry.FlagRemove = true;
                    return null;
                }
                return entry;
            }
            return null;
        }

        public static CacheEntry MakeEntry(HttpWebRequest request, HttpWebResponse response, List<Tuple<String, String>> headers, DateTime? expires)
        {
            CacheEntry newEntry = new CacheEntry();
            newEntry.Expires = expires;
            newEntry.DateStored = DateTime.Now;
            newEntry.Headers = headers;
            newEntry.AbsoluteUri = request.RequestUri.AbsoluteUri;
            newEntry.UserAgent = request.UserAgent;
            newEntry.StatusCode = response.StatusCode;
            newEntry.StatusDescription = response.StatusDescription;
            if (response.ContentLength > 0)
                newEntry.ResponseBytes = new Byte[response.ContentLength];
            return newEntry;
        }

        public static void AddData(CacheEntry entry)
        {
            if (!_cache.Contains(entry.AbsoluteUri) && !_cache.Contains(entry.UserAgent))
            {
                _cache.Add(entry.AbsoluteUri, entry);
                _cache.Add(entry.UserAgent, entry);
            }
        }

        public static Boolean CanCache(WebHeaderCollection headers, ref DateTime? expires)
        {

            foreach (String s in headers.AllKeys)
            {
                String value = headers[s].ToLower();
                switch (s.ToLower())
                {
                    case "cache-control":
                        if (value.Contains("max-age"))
                        {
                            int seconds;
                            if (int.TryParse(value, out seconds))
                            {
                                if (seconds == 0)
                                    return false;
                                DateTime d = DateTime.Now.AddSeconds(seconds);
                                if (!expires.HasValue || expires.Value < d)
                                    expires = d;

                            }
                        }

                        if (value.Contains("private") || value.Contains("no-cache"))
                            return false;
                        else if (value.Contains("public") || value.Contains("no-store"))
                            return true;

                        break;

                    case "pragma":

                        if (value == "no-cache")
                            return false;

                        break;
                    case "expires":
                        DateTime dExpire;
                        if (DateTime.TryParse(value, out dExpire))
                        {
                            if (!expires.HasValue || expires.Value < dExpire)
                                expires = dExpire;
                        }
                        break;
                }
            }
            return true;
        }

    }
}
